09. Using sessions in controllers

Using sessions in controllers Heading

Using sessions in controllers

ND004 C01 L05 10 Using Sessions In Controllers

Takeaways

  • Commits can succeed or fail. On fail, we want to rollback the session to avoid potential implicit commits done by the database on closing a connection.
  • Good practice is to close connections at the end of every session used in a controller, to return the connection back to the connection pool.

Pattern (try-except-finally)

 import sys

 try:
   todo = Todo(description=description)
   db.session.add(todo)
   db.session.commit()
 except:
   db.session.rollback()
   error=True
   print(sys.exc_info())
 finally:
   db.session.close()

Implementing try except finally in the app

ND004 C01 L05 10.1 Using Sessions In Controllers

Video Correction Notes

The route handler should always return something or raise an intentional exception, in the case of an error. To fix this with a simple solution, we can simply import abort from Flask:

from flask import abort

and we can call abort(<status code>) , e.g. with status code 500, abort(500) to rise an HTTPException for an Internal Server Error, in order to abort a request and prevent it from expecting a returned result. Since this is a course on web data modeling, we won't be going into errors in depth, but you can check out resources below.

Resources on Error Handling

Code (with corrections)

from flask import Flask, render_template, abort

# ...

@app.route('/todos/create', method=['POST'])
def create_todo():
  error = False
  body = {}
  try:
    description = request.form.get_json()['description']
    todo = Todo(description=description)
    db.session.add(todo)
    db.session.commit()
    body['description'] = todo.description
  except:
    error = True
    db.session.rollback()
    print(sys.exc_info())
  finally:
    db.session.close()
  if error:
    abort (400)
  else:
    return jsonify(body)

Creating an error

ND004 C01 L05 10.2 Using Sessions In Controllers

Practice

Implement this try-except-finally pattern in our create todo item route handler.

Starter Code

app.py
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://udacitystudios@localhost:5432/todoapp'
db = SQLAlchemy(app)

class Todo(db.Model):
  __tablename__ = 'todos'
  id = db.Column(db.Integer, primary_key=True)
  description = db.Column(db.String(), nullable=False)

  def __repr__(self):
    return f'<Todo {self.id} {self.description}>'

db.create_all()

@app.route('/todos/create', method=['POST'])
def create_todo():
  description = request.form.get_json()['description']
  todo = Todo(description=description)
  db.session.add(todo)
  db.session.commit()
  return jsonify({
    'description': todo.description
  })


@app.route('/')
def index():
  return render_template('index.html', data=Todo.query.all())
templates/index.html
<html>
  <head>
    <title>Todo App</title>
    <style>
      #error {
        display: none;
      }
    </style>
  </head>
  <body>
    <div id="error" class="hidden">Something went wrong!</div>
    <form id="form" method="post" action="/todos/create">
      <input type="text" id="description" name="description" />
      <input type="submit" value="Create" />
    </form>
    <ul id="todos">
      {% for d in data %}
      <li>{{ d.description }}</li>
      {% endfor %}
    </ul>
    <script>
      const descInput = document.getElementById('description');
      document.getElementById('form').onsubmit = function(e) {
        e.preventDefault();
        const desc = descInput.value;
        descInput.value = '';
        fetch('/todos/create', {
          method: 'POST',
          body: JSON.stringify({
            'description': desc,
          }),
          headers: {
            'Content-Type': 'application/json',
          }
        })
        .then(response => response.json())
        .then(jsonResponse => {
          console.log('response', jsonResponse);
          li = document.createElement('li');
          li.innerText = desc;
          document.getElementById('todos').appendChild(li);
          document.getElementById('error').className = 'hidden';
        })
        .catch(function() {
          document.getElementById('error').className = '';
        })
      }
    </script>
  </body>
</html>

Workspace

This section contains either a workspace (it can be a Jupyter Notebook workspace or an online code editor work space, etc.) and it cannot be automatically downloaded to be generated here. Please access the classroom with your account and manually download the workspace to your local machine. Note that for some courses, Udacity upload the workspace files onto https://github.com/udacity , so you may be able to download them there.

Workspace Information:

  • Default file path:
  • Workspace type: jupyter-lab
  • Opened files (when workspace is loaded): n/a